package com.moli.iov.notify.remote.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.common.base.Joiner;
import com.moli.iov.cache.RedisUtil;
import com.moli.iov.down.enums.OperateTypeEnum;
import com.moli.iov.exception.BusinessException;
import com.moli.iov.handle.alarm.enums.AlarmCodeCollectionEnum;
import com.moli.iov.handle.alarm.enums.AlarmCodeEnum;
import com.moli.iov.handle.alarm.enums.AlarmLevelEnum;
import com.moli.iov.notify.enums.*;
import com.moli.iov.notify.model.dto.*;
import com.moli.iov.notify.model.po.NoticeAlarm;
import com.moli.iov.notify.model.po.NoticeMail;
import com.moli.iov.notify.model.po.NoticeSms;
import com.moli.iov.notify.model.po.NoticeVehicleCtl;
import com.moli.iov.notify.remote.INoticeRemoteService;
import com.moli.iov.notify.service.INoticeAlarmService;
import com.moli.iov.notify.service.INoticeMailService;
import com.moli.iov.notify.service.INoticeSmsService;
import com.moli.iov.notify.service.INoticeVehicleCtlService;
import com.moli.iov.rest.RestResponse;
import com.moli.iov.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.Service;
import org.springframework.beans.factory.annotation.Autowired;

import javax.annotation.Resource;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.moli.iov.notify.constants.Constant.REDIS_NOTICE_MAIL_PREFIX;
import static com.moli.iov.notify.constants.Constant.REDIS_NOTICE_SMS_PREFIX;

/**
 *
 * Based on distributed Remote alarm and vehicle control notice service.
 *
 * @author OAK
 * @version 3.0
 * @since 2019/10/16 11:10 AM.
 *
 */
@Slf4j
@Service(group = "${service.group}")
public class NoticeRemoteServiceImpl implements INoticeRemoteService {

    @Resource
    private INoticeAlarmService noticeAlarmService;

    @Resource
    private INoticeVehicleCtlService noticeVehicleCtlService;

    @Resource
    private INoticeSmsService noticeSmsService;

    @Resource
    private INoticeMailService noticeMailService;

    @Autowired
    RedisUtil redisUtil;

    /**
     * According notice alarm data transfer object to query all notice alarm.
     * @param noticeAlarmDto notice alarm data transfer object
     * @return Whether success.
     */
    @Override
    public RestResponse<List<NoticeAlarmDto>> queryAllNoticeAlarmByProperties(NoticeAlarmDto noticeAlarmDto) {
        return RestResponse.success(noticeAlarmService.queryAllNoticeAlarmByProperties(noticeAlarmDto));
    }


    /**
     * According notice vehicle control data transfer object to query all notice vehicle control.
     * @param noticeVehicleCtlDto notice vehicle control data transfer object
     * @return Whether success.
     */
    @Override
    public RestResponse<List<NoticeVehicleCtlDto>> queryAllNoticeVehicleCtlByProperties(NoticeVehicleCtlDto noticeVehicleCtlDto) {
        return RestResponse.success(noticeVehicleCtlService.queryAllNoticeVehicleCtlByProperties(noticeVehicleCtlDto));
    }

    /**
     * According notice mail data transfer object to query email.
     * @param companyId Company id.
     * @param mailType Email type.
     * @param mailCode Email code.
     * @return Whether success.
     */
    @Override
    public RestResponse<String> queryMailByNoticeMail(Long companyId, Integer mailType, Integer mailCode) {
        NoticeMailDto noticeMailDto = new NoticeMailDto();
        noticeMailDto.setCompanyId(companyId);
        noticeMailDto.setMailType(mailType);
        noticeMailDto.setMailCode(mailCode);
        String mail = noticeMailService.queryMailToByNoticeMail(noticeMailDto);
        return RestResponse.success(mail);
    }

    /**
     * According notice sms data transfer object to query mobile.
     * @param companyId Company id.
     * @param smsType Sms type.
     * @param smsCode Sms code.
     * @return Whether success.
     */
    @Override
    public RestResponse<String> queryMobileByNoticeSms(Long companyId, Integer smsType, Integer smsCode) {
        NoticeSmsDto noticeSmsDto = new NoticeSmsDto();
        noticeSmsDto.setCompanyId(companyId);
        noticeSmsDto.setSmsType(smsType);
        noticeSmsDto.setSmsCode(smsCode);
        String mobile = noticeSmsService.queryMobileByNoticeSms(noticeSmsDto);
        return RestResponse.success(mobile);
    }

    /**
     * Add or remove cache source.
     * @param key Cache key.
     * @param sources Cache source collection value.
     * @param sourceType Cache source type.
     * @return Restful response status.
     */
    public RestResponse addOrRemoveCacheSource(String key, String [] sources, NoticeTypeEnum sourceType){
        RestResponse restResponse = new RestResponse();
        restResponse.setFlag(true);
        Set<String> cacheSet = new HashSet<>(),
                newCacheSet = new HashSet<>(),
                sourceSet = new HashSet<>(Arrays.asList(sources));
        try{
            if(sources==null ||sources.length<=0 || (sources.length==1&& "".equals(sources[0]))){
                redisUtil.delete(key);
                restResponse.setData(null);
                return restResponse;
            }

            if(redisUtil.exist(key)){
                Set<String> cacheSourceSet = redisUtil.getCacheSet(key);
                if(cacheSourceSet != null){
                    cacheSet.clear();
                    cacheSet.addAll(cacheSourceSet);
                    cacheSet.removeAll(sourceSet);
                    newCacheSet.clear();
                    newCacheSet.addAll(sourceSet);
                    newCacheSet.removeAll(cacheSourceSet);
                    if(cacheSet.isEmpty() && newCacheSet.isEmpty()) {
                        restResponse.setFlag(false);
                        restResponse.setMessage(String.format("该原始%s%s无变化。", sourceType.getText(), Joiner.on(",").join(sourceSet)));
                        return restResponse;
                    }
                }
                redisUtil.delete(key);
            }
            redisUtil.setCacheSet(key, sourceSet);
            restResponse.setData(Joiner.on(",").join(sourceSet));
            //redisUtil.expire(key, );
            return restResponse;
        }catch (Exception ex){
            log.error("Set mail cache found a fail, {}", ex);
            return restResponse;
        }
    }

    /**
     * Set cache send sms for mobile.
     * @param companyId Company id.
     * @param smsCode Sms code reference notice id.
     * @param smsType Sms type consist of one status code for alarm or two status code for vehicle control.
     * @param mobile Mobile.
     * @return Restful response status.
     */
    private RestResponse setCachedSMS(Long companyId, Integer smsCode, Integer smsType, String mobile){
        String key = String.format("%s%d%d%d", REDIS_NOTICE_SMS_PREFIX, companyId, smsCode, smsType);
        String [] allMobile = mobile.split(",");
        return addOrRemoveCacheSource(key, allMobile, NoticeTypeEnum.MOBILE);
    }

    /**
     * Set cache send mail for mail.
     * @param companyId Company id.
     * @param mailCode Mail code reference notice id.
     * @param mailType Mail type consist of one status code for alarm or two status code for vehicle control.
     * @param mail Email.
     * @return Restful response status.
     */
    private RestResponse setCachedMail(Long companyId, Integer mailCode, Integer mailType, String mail){
        String key = String.format("%s%d%d%d", REDIS_NOTICE_MAIL_PREFIX, companyId, mailCode, mailType);
        String [] allMail = mail.split(",");
        return addOrRemoveCacheSource(key, allMail, NoticeTypeEnum.EMAIL);
    }

    /**
     * According notice sms data transfer object to add notice sms.
     * @param noticeSmsDto notice sms data transfer object.
     * @return Restful response status.
     */
    @Override
    public RestResponse<String> addNoticeSms(NoticeSmsDto noticeSmsDto) {
        Long companyId = noticeSmsDto.getCompanyId();
        Integer smsCode = noticeSmsDto.getSmsCode(),
                smsType = noticeSmsDto.getSmsType();
        String mobile = noticeSmsDto.getMobile();
        RestResponse<String> restResponse = setCachedSMS(companyId, smsCode, smsType, mobile);
        if(!RestResponse.isSuccess(restResponse)){
            return RestResponse.failure(restResponse.getMessage());
        }
        QueryWrapper<NoticeSms> query = new QueryWrapper<>();
        query.eq("company_id", companyId).eq("sms_code", smsCode).eq("sms_type", smsType);
        NoticeSms noticeSms = noticeSmsService.getOne(query);
        if(noticeSms == null){
            noticeSms = new NoticeSms();
        }
        noticeSms.setStatus(StatusEnum.YES.getFlag());
        noticeSms.setMobile(restResponse.getData());
        noticeSms.setCompanyId(companyId.intValue());
        noticeSms.setSmsCode(smsCode);
        noticeSms.setSmsType(smsType);
        if(StringUtil.isNotBlank(noticeSms.getMobile())){
            boolean isSave = noticeSmsService.saveOrUpdate(noticeSms);
            if (isSave) {
                return RestResponse.success(String.format("添加手机号%s成功。", mobile));
            } else {
                return RestResponse.failure(String.format("添加手机号%s失败。", mobile));
            }
        }else{
           boolean isDelete = noticeSmsService.removeById(noticeSms.getId());
            if (isDelete) {
                return RestResponse.success(String.format("删除手机号%s成功。", mobile));
            } else {
                return RestResponse.failure(String.format("删除手机号%s失败。", mobile));
            }
        }
    }

    /**
     * According notice email data transfer object to add notice email.
     * @param noticeMailDto notice email data transfer object.
     * @return Restful response status.
     */
    @Override
    public RestResponse<String> addNoticeMail(NoticeMailDto noticeMailDto) {
        log.info("更新通知电子邮件,{}",noticeMailDto);
        Long companyId = noticeMailDto.getCompanyId();
        Integer mailCode = noticeMailDto.getMailCode(),
                mailType = noticeMailDto.getMailType();
        String mail = noticeMailDto.getMail();
        RestResponse<String> restResponse = setCachedMail(companyId, mailCode, mailType, mail);
        if(restResponse==null || !restResponse.flag){
            return RestResponse.failure(restResponse.getMessage());
        }
        QueryWrapper<NoticeMail> query = new QueryWrapper<>();
        query.eq("company_id", companyId).eq("mail_code", mailCode).eq("mail_type", mailType);
        NoticeMail noticeMail = noticeMailService.getOne(query);
        if(noticeMail == null){
            noticeMail = new NoticeMail();
        }
        noticeMail.setStatus(StatusEnum.YES.getFlag());
        noticeMail.setMailTo(restResponse.getData());
        noticeMail.setCompanyId(companyId.intValue());
        noticeMail.setMailCode(mailCode);
        noticeMail.setMailType(mailType);
        if(StringUtil.isNotBlank(noticeMail.getMailTo())){
            boolean isSave = noticeMailService.saveOrUpdate(noticeMail);
            if (isSave) {
                return RestResponse.success(String.format("添加电子邮件%s成功。", mail));
            } else {
                return RestResponse.failure(String.format("添加电子邮件%s失败。", mail));
            }
        }else{
            boolean isDelete = noticeMailService.removeById(noticeMail.getId());
            if (isDelete) {
                return RestResponse.success(String.format("删除电子邮件%s成功。", mail));
            } else {
                return RestResponse.failure(String.format("删除电子邮件%s失败。", mail));
            }
        }
    }

    /**
     * Draft sms used status and email used status communication.
     * @param noticeDto Sms and Email used status data transfer object.
     * @param status operator status.
     */
    private RestResponse<String> draft(NoticeDto noticeDto, Boolean status){
        Integer smsUsed = noticeDto.getSmsUsed(), mailUsed = noticeDto.getMailUsed();
        StringBuilder messageBuilder = new StringBuilder();
        if(smsUsed != null){
            if(smsUsed == SmsUsedEnum.DISABLED.getStatusCode()){
                messageBuilder.append(SmsUsedEnum.DISABLED.getStatusValue());
                messageBuilder.append(NoticeTypeEnum.MOBILE.getText());
            }else{
                messageBuilder.append(SmsUsedEnum.ENABLED.getStatusValue());
                messageBuilder.append(NoticeTypeEnum.MOBILE.getText());
            }
        }
        if(mailUsed != null){
            if(mailUsed == MailUsedEnum.DISABLED.getStatusCode()){
                messageBuilder.append(MailUsedEnum.DISABLED.getStatusValue());
                messageBuilder.append(NoticeTypeEnum.EMAIL.getText());
            }else{
                messageBuilder.append(MailUsedEnum.ENABLED.getStatusValue());
                messageBuilder.append(NoticeTypeEnum.EMAIL.getText());
            }
        }
        if (status) {
            messageBuilder.append(ResponseStatusEnum.SUCCESS.getStatusValue());
            return RestResponse.success(messageBuilder.toString());
        } else {
            messageBuilder.append(ResponseStatusEnum.FAILURE.getStatusValue());
            return RestResponse.failure(messageBuilder.toString());
        }
    }

    /**
     * Enabled Notice.
     * @param regex Pattern match all/sms/email.
     * @param noticeDto Notice data transfer object.
     * @throws BusinessException Business exception.
     */
    private void enabled(String regex, NoticeDto noticeDto) throws BusinessException {
        if(regex.toLowerCase().charAt(0) == ActionEnum.A.getCode().charValue()){
            noticeDto.setSmsUsed(SmsUsedEnum.ENABLED.getStatusCode());
            noticeDto.setMailUsed(MailUsedEnum.ENABLED.getStatusCode());
        }else if(regex.toLowerCase().charAt(0) == ActionEnum.S.getCode().charValue()){
            noticeDto.setSmsUsed(SmsUsedEnum.ENABLED.getStatusCode());
        }else if(regex.toLowerCase().charAt(0) == ActionEnum.E.getCode().charValue()){
            noticeDto.setMailUsed(MailUsedEnum.ENABLED.getStatusCode());
        }else{
            throw new BusinessException("URL路径参数不合法，启用发送短信与邮件/短信/邮件 只能传递a/s/e");
        }
    }

    /**
     * Disabled Notice.
     * @param regex Pattern match all/sms/email.
     * @param noticeDto Notice data transfer object.
     * @throws BusinessException Business exception.
     */
    private void disabled(String regex, NoticeDto noticeDto) throws BusinessException {
        if(regex.toLowerCase().charAt(0) == ActionEnum.A.getCode().charValue()){
            noticeDto.setSmsUsed(SmsUsedEnum.DISABLED.getStatusCode());
            noticeDto.setMailUsed(MailUsedEnum.DISABLED.getStatusCode());
        }else if(regex.toLowerCase().charAt(0) == ActionEnum.S.getCode().charValue()){
            noticeDto.setSmsUsed(SmsUsedEnum.DISABLED.getStatusCode());
        }else if(regex.toLowerCase().charAt(0) == ActionEnum.E.getCode().charValue()){
            noticeDto.setMailUsed(MailUsedEnum.DISABLED.getStatusCode());
        }else{
            throw new BusinessException("URL路径参数不合法，禁用发送短信与邮件/短信/邮件 只能传递a/s/e");
        }
    }

    /**
     *  Enabled vehicle control sms or email.
     * @param id Alarm notice id.
     * @param status Enabled all or sms or email.
     * @return Restful response status.
     */
    @Override
    public RestResponse<String> enableNoticeAlarm(Integer id, String status) {
        NoticeAlarmDto noticeAlarmDto = new NoticeAlarmDto();
        noticeAlarmDto.setId(id);
        enabled(status, noticeAlarmDto);
        return draft(noticeAlarmDto, noticeAlarmService.updateMailUsedAndSmsUsedById(noticeAlarmDto) );
    }

    /**
     *  Disabled vehicle control sms or email.
     * @param id Alarm notice id.
     * @param status Disabled all or sms or email.
     * @return Restful response status.
     */
    @Override
    public RestResponse<String> disabledNoticeAlarm(Integer id, String status) {
        NoticeAlarmDto noticeAlarmDto = new NoticeAlarmDto();
        noticeAlarmDto.setId(id);
        disabled(status, noticeAlarmDto);
        return draft(noticeAlarmDto, noticeAlarmService.updateMailUsedAndSmsUsedById(noticeAlarmDto) );
    }

    /**
     *  Enabled vehicle control sms or email.
     * @param id Vehicle control notice id.
     * @param status Enabled all or sms or email.
     * @return Restful response status.
     */
    @Override
    public RestResponse<String> enabledNoticeVehicleCtl(Integer id, String status) {
        NoticeVehicleCtlDto noticeVehicleCtlDto = new NoticeVehicleCtlDto();
        noticeVehicleCtlDto.setId(id);
        enabled(status, noticeVehicleCtlDto);
        return draft(noticeVehicleCtlDto, noticeVehicleCtlService.updateMailUsedAndSmsUsedById(noticeVehicleCtlDto) );
    }

    /**
     *  Disabled vehicle control sms or email.
     * @param id Vehicle control notice id.
     * @param status Disabled all or sms or email.
     * @return Restful response status.
     */
    @Override
    public RestResponse<String> disabledNoticeVehicleCtl(Integer id, String status) {
        NoticeVehicleCtlDto noticeVehicleCtlDto = new NoticeVehicleCtlDto();
        noticeVehicleCtlDto.setId(id);
        disabled(status, noticeVehicleCtlDto);
        return draft(noticeVehicleCtlDto, noticeVehicleCtlService.updateMailUsedAndSmsUsedById(noticeVehicleCtlDto) );
    }

    /**
     * According company id to add default vehicle control and alarm notice.
     * @param companyId Company id.
     * @return Whether success.
     */
    @Override
    public RestResponse<Boolean> addDefaultNotice(Long companyId) {
        final Date currentTime = new Date();
        final Integer smsUsed = SmsUsedEnum.ENABLED.getStatusCode(),
                mailUsed = MailUsedEnum.ENABLED.getStatusCode(),
                status = StatusEnum.NO.getFlag();
        List<NoticeAlarm> noticeAlarmList = new ArrayList<>();
        Stream.of(AlarmCodeCollectionEnum.values()).forEach(alarmCodeCollectionEnum -> {
            AlarmLevelEnum [] alarmLevelEnums = alarmCodeCollectionEnum.getLevels();
            noticeAlarmList.addAll(Stream.of(alarmLevelEnums).map(alarmLevelEnum ->
                    new NoticeAlarm(alarmCodeCollectionEnum.getFlag(),
                             alarmLevelEnum.getFlag(), mailUsed, smsUsed, currentTime, currentTime, status, companyId)
            ).collect(Collectors.toList()));
        });
        List<NoticeVehicleCtl> noticeVehicleCtlList = OperateTypeEnum.getCtlVehicleEnums().stream().map(operateTypeEnum ->
                new NoticeVehicleCtl(
                operateTypeEnum.getFlag(), mailUsed, currentTime, currentTime, status, companyId, smsUsed
        )).collect(Collectors.toList());
        boolean nAlarmRes = noticeAlarmService.saveBatch(noticeAlarmList);
        boolean nCtlRes = noticeVehicleCtlService.saveBatch(noticeVehicleCtlList);
        return RestResponse.success(nAlarmRes && nCtlRes);
    }

}
